<script setup lang="ts">
import { onMounted, watch } from "vue";
import { ElMessage } from "element-plus";
import Guacamole from "guacamole-common-js";
import { useVaStoreHook } from "@/store/modules/va";
import { ElNotification } from "element-plus";
import {
  getAppIssueInfo,
  bindWinSession,
  insertAppOpenLog
} from "@/api/vap/cluster";
import Toolbar from "./toolbar.vue";
import { ChannelMsgForAppParams, ChannelMsg } from "@/views/desktop/guacd_code";

defineOptions({
  name: "Desktop"
});
const vaStore = useVaStoreHook();
const desktopObj = {
  client: undefined,
  layer: undefined,
  state: 0,
  sink: undefined
};

const getQueryParams = (url: string) => {
  const paramArr = url.slice(url.indexOf("?") + 1).split("&");
  const params = new Map();
  paramArr.map(param => {
    const [key, val] = param.split("=");
    params.set(key, decodeURIComponent(val));
  });
  return params;
};

onMounted(() => {
  const sessionId = "1";
  // // 远程桌面画布的大小
  const width = document.body.clientWidth - 1;
  const height = document.body.clientHeight;
  console.log(width, height, sessionId);
  wsHandshake();
  // // 监听窗口大小变化，执行窗口重绘操作
  // window.addEventListener('resize', onWindowResize)
  // window.onfocus = onWindowFocus
});
const websocketPath = "ws://" + window.location.host + "/api/vap";

const wsHandshake = () => {
  const param = getQueryParams(window.location.href);
  console.log(param);

  const width = document.body.clientWidth - 1;
  const height = document.body.clientHeight;
  const params =
    "hostSessionId=" +
    param.get("id") +
    "&isReuse=" +
    param.get("isReuse") +
    "&appId=" +
    param.get("appId") +
    "&width=" +
    width +
    "&height=" +
    height +
    "&dpi=" +
    90;
  // todo 设置虚拟机剪贴板内容

  const tunnel = new Guacamole.WebSocketTunnel(
    websocketPath + "/session/gua/tunnel"
  );
  tunnel.onerror = onTunnelError;
  // 通道状态改变时的回调函数
  tunnel.onstatechange = onTunnelStateChange;
  // 创建桌面客户端
  desktopObj.client = new Guacamole.Client(tunnel);
  // 解析自定义指令，这行代码必须放到  new Guacamole.Client(tunnel) 代码之后
  const _oninstruction = tunnel.oninstruction;
  tunnel.oninstruction = function (opcode, parameters) {
    onTunnelInstruction(opcode, parameters);
    if (_oninstruction) {
      _oninstruction(opcode, parameters);
    }
  };

  desktopObj.client.onerror = onClientError;
  // 处理客户端的状态变化事件
  desktopObj.client.onstatechange = onClientStateChange;

  desktopObj.client.onpipe = onPipe;
  const display = document.getElementById("display");
  desktopObj.layer = desktopObj.client.getDisplay();
  const element = desktopObj.client.getDisplay().getElement();
  display.appendChild(element);
  desktopObj.client.connect(params);
  // 页面关闭时销毁客户端对象
  window.onunload = function () {
    desktopObj.client.disconnect();
  };

  // mouse
  const mouse = new Guacamole.Mouse(element);
  mouse.onmousedown =
    mouse.onmouseup =
    mouse.onmousemove =
      function (mouseState: any) {
        const ms = new Guacamole.Mouse.State(
          mouseState.x / desktopObj.layer.getScale(),
          mouseState.y / desktopObj.layer.getScale(),
          mouseState.left,
          mouseState.middle,
          mouseState.right,
          mouseState.up,
          mouseState.down
        );
        desktopObj.client.sendMouseState(ms);
      };
  // sink
  desktopObj.sink = new Guacamole.InputSink();
  display.appendChild(desktopObj.sink.getElement());
  desktopObj.sink.focus();

  // Keyboard
  const keyboard = new Guacamole.Keyboard(desktopObj.sink.getElement());
  keyboard.onkeydown = onKeyDown;
  keyboard.onkeyup = onKeyUp;
};
const onClientError = (state: any) => {
  console.log("onClientError", state);
};
const onClientStateChange = (state: any) => {
  console.log("onClientStateChange", state);
  const param = getQueryParams(window.location.href);
  if (
    state == 3 &&
    (param.get("isOpen") == "true" || param.get("isReuse") == "true")
  ) {
    VCMsgForOpenAppForReuseSession();
  }
};

const onTunnelError = (state: any) => {
  console.log("onTunnelError", state);
};

const onTunnelStateChange = (state: any) => {
  console.log("onTunnelStateChange", state);
};

const onTunnelInstruction = (opcode: any, parameters: any) => {
  if (opcode == "hint") {
    ElNotification({
      title: decodeURI(parameters[0]),
      message: decodeURI(parameters[1]),
      type: parameters[2],
      duration: 0
    });
  }
  if (opcode == "disconnect") {
    console.log("连接销毁，画面关闭");
  }
  if (opcode == "error") {
    console.error("指令", opcode, "参数", parameters);
    desktopObj.client.disconnect();
  }
};
const onPipe = (input, mimetype, name) => {
  console.log("onPipe >>", input, mimetype, name);
  if (name !== "LAUNCH") {
    return;
  }
  const reader = new Guacamole.StringReader(input);
  reader.ontext = function (text) {
    try {
      console.log(text);
      const vmsg = JSON.parse(text);
      const param = getQueryParams(window.location.href);
      let data5: any = {};
      switch (vmsg.code) {
        // 打开通道后发送出去的第一个信号
        case 1:
          // 通知go绑定会话id，同时发送信号，打开应用
          bindWinSession(param.get("id"), vmsg.data);
          if (param.get("isOpen") == "true") {
            VCMsgForOpenApp();
          }
          break;
        // 打开应用成功
        case 5:
          //nolint
          data5 = JSON.parse(vmsg.data);
          insertAppOpenLog(data5.appId, data5.hostSessionId);
          break;
        default:
          console.log("未知的消息类型：", vmsg);
      }
    } catch (e) {
      console.log("解析LAUNCH参数失败", e);
    }
  };
};

watch(
  () => vaStore.openAppId,
  (newValue, oldValue) => {
    console.log("value 变化了:", newValue, "旧的值:", oldValue);
    const param = getQueryParams(window.location.href);
    getAppIssueInfo(newValue).then(res => {
      const params: ChannelMsgForAppParams = {
        hostSessionId: param.get("id"),
        appId: newValue,
        exePath: res.execPath,
        workDir: res.workSpace,
        params: [res.appParams]
      };
      const msg: ChannelMsg = {
        code: 2, // 打开应用
        data: JSON.stringify(params)
      };
      ElMessage({
        message: "准备打开应用【" + res.alias + "】",
        type: "warning"
      });
      const writer = new Guacamole.StringWriter(
        desktopObj.client.createPipeStream("application/octet-stream", "LAUNCH")
      );
      writer.sendText(JSON.stringify(msg));
    });
  }
);

const VCMsgForOpenApp = () => {
  const param = getQueryParams(window.location.href);
  getAppIssueInfo(param.get("appId")).then(res => {
    const params: ChannelMsgForAppParams = {
      hostSessionId: param.get("id"),
      appId: param.get("appId"),
      exePath: res.execPath,
      workDir: res.workSpace,
      params: [res.appParams]
    };
    const msg: ChannelMsg = {
      code: 2, // 打开应用
      data: JSON.stringify(params)
    };
    ElMessage({
      message: "准备打开应用【" + res.alias + "】",
      type: "warning"
    });
    const writer = new Guacamole.StringWriter(
      desktopObj.client.createPipeStream("application/octet-stream", "LAUNCH")
    );
    writer.sendText(JSON.stringify(msg));
  });
};

const VCMsgForOpenAppForReuseSession = () => {
  console.log("通过虚拟通道发送打开应用的消息");
  const param = getQueryParams(window.location.href);
  getAppIssueInfo(param.get("appId")).then(res => {
    const params: ChannelMsgForAppParams = {
      hostSessionId: param.get("id"),
      appId: param.get("appId"),
      exePath: res.execPath,
      workDir: res.workSpace,
      params: [res.appParams]
    };
    const msg: ChannelMsg = {
      code: 2, // 打开应用
      data: JSON.stringify(params)
    };
    ElMessage({
      message: "准备打开应用【" + res.alias + "】",
      type: "warning"
    });
    const writer = new Guacamole.StringWriter(
      desktopObj.client.createPipeStream("application/octet-stream", "LAUNCH")
    );
    writer.sendText(JSON.stringify(msg));
  });
};

const onKeyUp = keySym => {
  desktopObj.client.sendKeyEvent(0, keySym);
};
const onKeyDown = keySym => {
  console.log(keySym);
  desktopObj.client.sendKeyEvent(1, keySym);
  if (keySym === 65288) {
    return false;
  }
};
</script>

<template>
  <div id="display" class="desktop-div" />
  <Toolbar />
</template>

<style scoped>
.desktop-div {
  width: 100%;
  height: 100%;
}
</style>
